package org.zn.note.jdk.container;

import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

/**
 * {@link CopyOnWriteArrayList} ArrayList线程安全的版本
 * 与ArrayList的不同
 * 一、private transient volatile Object[] array;
 * ArrayList没用volatile
 * "volatile" 易变的，volatile修饰的变量不允许线程内部缓存和重排序，即直接修改内存。
 * 二、读操作(get)和ArrayList差不多，是没有锁的
 * 写操作(add\set)，不同，比如 add(E e)
 * 1、加锁 lock.lock();
 * 2、在新数组上写
 * Object[] newElements = Arrays.copyOf(elements, len + 1);
 * newElements[len] = e;
 * 3、array指向新数组  array = newElements
 * 4、释放锁 lock.unlock();
 */
public class JdkCopyOnWriteArrayList<E>
//        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

    final transient ReentrantLock lock = new ReentrantLock();
    // 规定只能通过get/set来访问
    private transient volatile Object[] array;

    final Object[] getArray() {
        return array;
    }

    final void setArray(Object[] a) {
        array = a;
    }

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);

            if (oldValue != element) {
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len);
                newElements[index] = element;
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

    private E get(Object[] a, int index) {
        return (E) a[index];
    }
}
